<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"><html lang="en">
<HEAD>

<meta name="copyright" content="Copyright (c) IBM Corporation and others 2000, 2005. This page is made available under license. For full details see the LEGAL in the documentation book that contains this page." >

<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1">
<META HTTP-EQUIV="Content-Style-Type" CONTENT="text/css">

<LINK REL="STYLESHEET" HREF="../book.css" CHARSET="ISO-8859-1" TYPE="text/css">
<TITLE>
Workspace save participation
</TITLE>

<link rel="stylesheet" type="text/css" HREF="../book.css">
</HEAD>
<BODY BGCOLOR="#ffffff">
<H2>
Workspace save participation</H2>
<P >
Workspace save processing is triggered when the workbench is shut down by the user and at other times
periodically by the platform.&nbsp; Plug-ins can participate in the workspace save process so that critical
plug-in data is saved to disk whenever the rest of the workspace's persistent data is saved. </P>
<P >
The workspace save process can also be used to track changes that occur between activations of your plug-in.</P>

<H3>
Implementing a save participant</H3>
<P >
To participate in workspace saving, you must add a save participant to the workspace. This is typically done during your plug-in's startup method.&nbsp;
This is also where you read any state that you might have saved
when your plug-in was last shut down.</P>
<P >
Let's look at a simple plug-in which will demonstrate the save process.</P>
<pre>
   package com.example.saveparticipant;

   import org.eclipse.core.runtime.*;
   import org.eclipse.core.resources.*;
   import java.io.File;
   import java.util.*;

   public class MyPlugin extends Plugin {
      private static MyPlugin plugin;

      public MyPlugin(IPluginDescriptor descriptor) {
         super(descriptor);
         plugin = this;
      }

      public static MyPlugin getDefault() {
         return plugin;
      }

      protected void readStateFrom(File target) {
      }

      public void startup() throws CoreException {
         super.startup();
         ISaveParticipant saveParticipant = new MyWorkspaceSaveParticipant();
         ISavedState lastState =
            <b>ResourcesPlugin.getWorkspace().addSaveParticipant(this, saveParticipant);</b>
         if (lastState == null)
            return;
         IPath location = lastState.lookup(new Path(&quot;save&quot;));
         if (location == null)
            return;
         // the plugin instance should read any important state from the file.
         File f = getStateLocation().append(location).toFile();
         readStateFrom(f);
      }

      protected void writeImportantState(File target) {
      }
   }</pre>
<P >
<b><a href="../reference/api/org/eclipse/core/resources/ISaveParticipant.html">
ISaveParticipant</a></b> defines the protocol for a workspace save participant. Implementors of this
interface can provide behavior for different stages of the save process.&nbsp;
Let's look at the stages and how our class <b>WorkspaceSaveParticipant</b>
implements each of these steps.</P>
<ul>
  <li>
<b>
prepareToSave</b> notifies the participant that the workspace is about to be saved and that it should suspend normal operation until further notice.&nbsp;
Our save particpant does nothing here.</li>
</ul>
<pre>
      public void prepareToSave(ISaveContext context) throws CoreException {
      }
</pre>
<ul>
  <li>
<b>
saving</b> tells the participant to save its important state.</li>
</ul>
<pre>
      public void saving(ISaveContext context) throws CoreException {
         switch (context.getKind()) {
            case ISaveContext.FULL_SAVE:
               MyPlugin myPluginInstance = MyPlugin.getDefault();
               // save the plug-in state
               <b>int saveNumber = context.getSaveNumber();
               String saveFileName = &quot;save-&quot; + Integer.toString(saveNumber);
               File f = myPluginInstance.getStateLocation().append(saveFileName).toFile();
               </b>// if we fail to write, an exception is thrown and we do not update the path
               <b>myPluginInstance.writeImportantState(f);
               context.map(new Path(&quot;save&quot;), new Path(saveFileName));
               context.needSaveNumber();</b>
               break;
            case ISaveContext.PROJECT_SAVE:
               // get the project related to this save operation
               IProject project = context.getProject();
               // save its information, if necessary
               break;
            case ISaveContext.SNAPSHOT:
               // This operation needs to be really fast because
               // snapshots can be requested frequently by the
               // workspace.
               break;
         }
      }
</pre>
<blockquote>
  <p>The <a href="../reference/api/org/eclipse/core/resources/ISaveContext.html"><b> ISaveContext</b></a> describes information about the save operation.&nbsp;
There are three kinds of save operations:&nbsp; <b>FULL_SAVE</b>,
<b>SNAPSHOT</b>, and <b>PROJECT_SAVE</b>.&nbsp;
Save participants should be careful to perform the processing appropriate for
the kind of save event they have received.&nbsp; For example, snapshot events
may occur quite frequently and are intended to allow plug-ins to save their
critical state.&nbsp; Taking a long time to save state which can be recomputed
in the event of a crash will slow down the platform.</p>
<P >
A save number is used to create data save files that are named using sequential numbers (<b>save-1</b>,
<b>save-2</b>, etc.)&nbsp; Each save file is mapped to a logical file name (<b>save</b>) that is independent of the save number. Plug-in data is written to the corresponding file and can be retrieved later without knowing the specific save number of the last successful save operation.&nbsp;
Recall that we saw this technique in our&nbsp; plug-in's startup code:</P>
<pre>
<b>IPath location = lastState.lookup(new Path(&quot;save&quot;));</b>
</pre>
<P >
After we have saved our data and mapped the file name, we call <b>needSaveNumber</b> 
to indicate that we have actively participated in a workspace save and want to assign a number to the save activity.
The save numbers can be used to create data files as above.&nbsp;</P>


</blockquote>
<ul>
  <li>
<b>
doneSaving</b> notifies the participant that the workspace has been saved and the participant can continue normal operation.</li>
</ul>
<pre>
      public void doneSaving(ISaveContext context) {
         MyPlugin myPluginInstance = MyPlugin.getDefault();

         // delete the old saved state since it is not necessary anymore
         int previousSaveNumber = <b>context.getPreviousSaveNumber()</b>;
         String oldFileName = &quot;save-&quot; + Integer.toString(previousSaveNumber);
         File f = myPluginInstance.getStateLocation().append(oldFileName).toFile();
         f.delete();
      }
</pre>
<blockquote>
  <p>Here, we clean up the save information from the previous save
  operation.&nbsp; We use <b>getPreviousSaveNumber</b> 
  to get the save number that was assigned in the previous save operation (not
  the one we just completed).&nbsp; We use this number to construct the name of
  the file that we need to delete.&nbsp; Note that we do not use the save
  state's logical file map since we've already mapped our current save file
  number.&nbsp;</p>
</blockquote>
<ul>
  <li>
<b>
rollback</b> tells the participant to rollback the important state because the save operation has failed.</li>
</ul>
<pre>
      public void rollback(ISaveContext context) {
         MyPlugin myPluginInstance = MyPlugin.getDefault();

         // since the save operation has failed, delete the saved state we have just written
         int saveNumber = context.getSaveNumber();
         String saveFileName = &quot;save-&quot; + Integer.toString(saveNumber);
         File f = myPluginInstance.getStateLocation().append(saveFileName).toFile();
         f.delete();
      }
</pre>
<blockquote>
  <p>Here, we delete the state that we just saved.&nbsp; Note that we use the
  current save number to construct the file name of the file we just saved.&nbsp;
  We don't have to worry about the fact that we mapped this file name into the <a href="../reference/api/org/eclipse/core/resources/ISaveContext.html"><b> ISaveContext</b></a><b>.
  </b>The platform will discard the context when a save operation fails.</p>
</blockquote>


<p>If your plug-in throws an exception at any time during the save lifecycle, it
will be removed from the current save operation and will not get any of the
remaining lifecycle methods.&nbsp; For example, if you fail during your <b>saving</b> 
method, you will not receive a
<b>
rollback</b>  or
<b>
doneSaving</b>  message.&nbsp;</p>


<H3>
Using previously saved state</H3>
<P >
When you add a save participant to the workspace, it will return an <b><a href="../reference/api/org/eclipse/core/resources/ISavedState.html"> ISavedState</a></b> 
object, which describes what your plug-in saved during its last save operation (or
<b> null</b> if your plug-in has not previously saved any state). This
object can be used to access information from the previous save file (using the save number and file map) or to process changes that have occurred between activations of a plug-in.</P>

<H4>
Accessing the save files</H4>
<P >
If a file map was used to save logically named files according to the save number, this same map can be used to retrieve the data from the last known save state.</P>
<pre>
   ISaveParticipant saveParticipant = new MyWorkspaceSaveParticipant();
   ISavedState lastState =
      ResourcesPlugin.getWorkspace().addSaveParticipant(myPluginInstance, saveParticipant);

   if (lastState != null) {
      String saveFileName = lastState.lookup(new Path(&quot;save&quot;)).toString();
      File f = myPluginInstance.getStateLocation().append(saveFileName).toFile();
      // the plugin instance should read any important state from the file.
      myPluginInstance.readStateFrom(f);
   }
</pre>


<H4>
Processing resource deltas between activations</H4>
<P >
Recall that any number of resource change events could occur in the workspace before your plug-in is ever activated. If you want to know what changes have occurred since your plug-in was
deactivated, you can use the save mechanism to do so, even if you don't need to save any other data.</P>
<P >
The save participant must request that the platform keep a resource delta on its behalf. This is done as part of the save operation.</P>
<pre>
   public void saving(ISaveContext context) throws CoreException {
      // no state to be saved by the plug-in, but request a
      // resource delta to be used on next activation.
      context.needDelta();
   }
</pre>
<P >
During plug-in startup, the previous saved state can be accessed and change events will be created for all changes that have occurred since the last save.</P>
<pre>
   ISaveParticipant saveParticipant = new MyWorkspaceSaveParticipant();
   ISavedState lastState =
      ResourcesPlugin.getWorkspace().addSaveParticipant(myPluginInstance, saveParticipant);
   if (lastState != null) {
      lastState.processResourceChangeEvents(new MyResourceChangeReporter());
   }
</pre>
<P >
The provided class must implement <a href="../reference/api/org/eclipse/core/resources/IResourceChangeListener.html"><b>IResourceChangeListener</b></a>, as described in
<a HREF="resAdv_events.htm" CLASS="XRef"> Tracking resource changes</a>.&nbsp; The changes since the last save are reported as part of the <b> POST_AUTO_BUILD</b> resource change event.</P>
<blockquote><i>
Note:&nbsp; Marker changes are not reported in the change events stored in an <b> ISavedState</b>. 
You must assume that any or all markers have changed since your last state was saved.</i></blockquote>

</BODY>
</HTML>
